package com.zmh.service.httpclient;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.zmh.dao.BookDao;
import com.zmh.dao.BookLinkDao;
import com.zmh.model.book.BookFav;
import com.zmh.model.book.BookInfo;

/**
 * @author zhoumh
 * @version 创建时间：2017年7月19日 下午4:31:12
 * @Description  还是要做修改，多次调用，获取所有链接，做302跳转之后的地址
 */
@Service
public class BookLink {
	private static Logger logger = LoggerFactory.getLogger(BookLink.class);
	@Autowired
	private BookLinkDao bookLinkDao;
	@Autowired
	private BookDao bookDao;
	@Autowired
	private QiDian qiDian;
	@Autowired
	private HttpConnectionManager httpConnectionManager;
	private static CloseableHttpClient httpClient = null;
	private static CloseableHttpResponse response = null; 
	private static HttpClientContext httpContext = HttpClientContext.create();
	@PostConstruct
	public void init(){
		getHttpClient();
	}
	@PreDestroy  
    public void  dostory(){  
		closeHttpClient();
	}
	/**
	 * 获取booklink的书架信息，与数据库进行比对找出有更新的书籍
	 * @author zhoumh
	 * @version 创建时间：2017年10月8日 下午4:35:03
	 */
	public List<BookFav> getUpdateBook() {
		//定义一个有更新的书籍列表
		List<BookFav> updateFavs = new ArrayList<>();
		//登陆booklink
		if (!login()) {
			//如果登陆失败终止后续程序执行
			logger.error("登陆boollink失败，本次运行终止。");
			return null;
		}
		// 请求booklink书架
		String url = "https://booklink.me/fav_book.php";
		String html = getHTML(url);
		// 获取booklink最新的书籍信息
		List<BookFav> newFavs = getBookFav(html);
		// 获取存储的最新书架信息根据用户UUID
		Map<String, BookFav> oldFavs = bookLinkDao.queryBookByUserAll("l2096h4d-0b74-72a1-7086-5168dj3ca7a9");
		for (BookFav newFav : newFavs) {
			BookFav oldFav = oldFavs.get(newFav.getBid());
			newFav.setUser("l2096h4d-0b74-72a1-7086-5168dj3ca7a9");
			//如果booklink上的书籍不在书架上就增加
			if (null == oldFav || oldFav.getUuid().equals("")) {
				bookLinkDao.insertBookFav(newFav);
				//新增加书籍的同时加入待通知更新列表中
				updateFavs.add(newFav);
				continue;
			}
			//如果保存的书架不是最新章节就修改最新章节信息并加入待通知更新列表中
			if (!newFav.getNstatus().equals(oldFav.getNstatus())) {
				if(bookLinkDao.updateBookByBid(newFav)!=1){
					logger.error("更新失败！ "+JSON.toJSONString(newFav));
				}
				updateFavs.add(newFav);
			}
		}
		//到此为止直接返回待通知列表，然后交给上级业务类调用微信服务接口发给用户
		logger.info(JSON.toJSONString(updateFavs));
//		System.out.println(html);
		return updateFavs;
	}
	/**
	 * 根据URL发起请求并返回请求结果
	 * @param url 请求链接
	 * @author zhoumh
	 * @version 创建时间：2017年10月13日 上午10:30:47
	 */
	private String getHTML(String url) {
		String html = "";
		try {
			response = doGet(url);
			HttpEntity entity = response.getEntity();
			// 判断返回实体是否为空
			if (entity != null) {
				// 获取返回的内容
				html = EntityUtils.toString(entity, "UTF-8");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return html;
	}
	/**
	 * 登陆BookLink
	 * @author zhoumh
	 * @version 创建时间：2017年10月12日 下午5:32:23
	 * @throws IOException 
	 * @throws ClientProtocolException 
	 */
	private boolean login(){
		boolean result = false;
		CloseableHttpResponse response = null;
		// 设置POST参数
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		formparams.add(new BasicNameValuePair("username", "wwwxeb"));
		formparams.add(new BasicNameValuePair("passwd", "8303523"));
		formparams.add(new BasicNameValuePair("cookie", "4"));
		UrlEncodedFormEntity uefEntity;
		try {
			uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
			// 发起登陆请求
			response = doPost("https://booklink.me/after_login.php", uefEntity);
			if (response.getStatusLine().getStatusCode()==200) {
				result = true;
			}
		} catch (Exception e) {
			result = false;
			e.printStackTrace();
		} finally {
			// 关闭或释放资源
			try {
				response.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
//		logger.info("登陆请求结果: "+result);
		return result;
	}
	/**
	 * 获取最新书架信息
	 * @param html 网页内容
	 * @return: List<BookFav> 书架清单
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 下午2:08:42 
	 * @Description: 目前根据书名查询UUID存在一定问题，以后增加书名+作者判断
	 */
	private List<BookFav> getBookFav(String html) {
		//对返回的书架内容做解析，将每本书的名称、章节信息提取出来
		Document doc = Jsoup.parse(html);
		//选择书籍所在行
		Elements links = doc.select("tr[bgcolor=#fffff0],tr[bgcolor=#f0f0f0]");
		List<BookFav> bookFavs = new ArrayList<>();
		for (Element element : links) {
			BookFav bookFav = new BookFav();
			//书籍链接
			String nurl = element.select("a[href^=/book]").select("a[rel=nofollow]").attr("href");
			//如果没有书籍链接，就跳过
			if (nurl.length()<1) {
				continue;
			}
			//书名
			String name = element.select("a[href^=/book]").select("a[rel!=nofollow]").text();
			//最新章节
			String nstatus = element.select("a[href^=/book]").select("a[rel=nofollow]").text();
			//根据书名查询书籍UUID
			BookInfo book = bookDao.queryBookByName(name);
			if (null==book || book.getUuid().equals("")) {
				//如果没有查找到书名就增加书籍信息
				if (addBookInfo(element)) {
					book = bookDao.queryBookByName(name);
				}else{
					logger.error("添加书籍信息失败! "+name);
				}
			}
			bookFav.setBid(book.getUuid());
			bookFav.setBname(name);
			bookFav.setNstatus(nstatus);
			bookFav.setNurl("https://booklink.me"+nurl);
			bookFavs.add(bookFav);
//			logger.info(JSON.toJSONString(bookFav));
		}
		return bookFavs;
	}
	/**
	 * 增加书籍信息
	 * @param Element html节点
	 * @return: boolean 是否增加成功
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 上午11:31:22 
	 * @Description:
	 */
	private boolean addBookInfo(Element element) {
		//返回值定义
		boolean result = true;
		String href = element.select("a[href^=/jump_c.php]").attr("href");
		try {
			doGet("https://booklink.me"+href);
		} catch (Exception e) {
			result = false;
			logger.error("请求书籍真实链接信息失败！");
			e.printStackTrace();
		}
		URI uri = httpContext.getRedirectLocations().get(0);
		String host = uri.getHost();
		String bookUrl = uri.toString();
		String[] bookPath = uri.getPath().split("/");
		//根据书籍源站地址使用不同的处理类，目前只有起点，其他暂时使用通用处理类
		if ("vipreader.qidian.com".equals(host)) {
			bookUrl = "https://book.qidian.com/info/"+bookPath[2];
			//调用起点处理类增加书籍信息
			result = qiDian.addBookByUrl(bookUrl);
		}
		if ("book.zongheng.com".equals(host)) {
			bookUrl = "http://book.zongheng.com/book/"+bookPath[2];
			result = addBook(element);
		}
		if ("www.motie.com".equals(host)) {
			bookUrl = bookUrl.substring(0, bookUrl.lastIndexOf("_"));
			result = addBook(element);
		}
		if ("yc.ireader.com.cn".equals(host)) {
			result = addBook(element);
		}
		if ("chuangshi.qq.com".equals(host)) {
			result = addBook(element);
		}
		if ("www.tadu.com".equals(host)) {
			result = addBook(element);
		}
		return result;
	}
	/**
	 * 起点之外的书籍信息暂时这样处理
	 * @param element
	 * @return: boolean
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 下午2:06:58 
	 * @Description:
	 */
	private boolean addBook(Element element) {
		boolean result = true;
		BookInfo book = new BookInfo();
		// 设置书名
		book.setName(element.select("a[href^=/book]").get(0).text());
		book.setUrl(element.select("a[href^=/jump_c.php]").attr("href"));
		book.setAuthor(element.select("a[href^=/author]").text());
		if (bookDao.insertBook(book)!=1) {
			//如果返回值不为1就说明增加失败
			result = false;
		}
		return result;
	}
	/**
	 * 根据传入的URL和httpclient发起get请求并返回结果
	 * @param url 请求的地址
	 * @throws ClientProtocolException
	 * @throws IOException 
	 * @return: CloseableHttpResponse
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 上午11:04:45 
	 * @Description:
	 */
	private CloseableHttpResponse doGet(String url)
			throws ClientProtocolException, IOException {
		HttpGet httpGet = new HttpGet(url);
		// 配置请求的HEADER部分
		httpGet.addHeader(HttpHeaders.ACCEPT, "application/xml");
		// 接收返回信息
		response = getHttpClient().execute(httpGet, httpContext);
		return response;
	}
	/**
	 * 根据传入的URL和httpclient发起POST请求并返回结果
	 * @param url 请求的地址
	 * @param uefEntity 请求的表单
	 * @throws ClientProtocolException
	 * @throws IOException 
	 * @return: CloseableHttpResponse
	 * @author: zhoumh
	 * @version: 创建时间：2017年7月26日 下午5:19:23
	 * @Description: 尝试超时捕捉并切换url重试
	 */
	private CloseableHttpResponse doPost(String url, UrlEncodedFormEntity uefEntity)
			throws ClientProtocolException, IOException {
		// Post请求
		HttpPost httppost = new HttpPost(url);
		httppost.setEntity(uefEntity);
		// 发送请求并接收返回信息
		response = getHttpClient().execute(httppost,httpContext);
		return response;
	}
	/**
	 * 从连接池获取httpclient
	 * @return: CloseableHttpClient
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 上午9:45:28 
	 * @Description:
	 */
	private CloseableHttpClient getHttpClient(){
		if (httpClient==null) {
			httpClient = httpConnectionManager.getHttpsClient();
		}
		return httpClient;
	}
	/**
	 * 关闭httpclient
	 * @return: void
	 * @author: zhoumh
	 * @version: 创建时间：2017年10月17日 上午9:45:50 
	 * @Description:
	 */
	private void closeHttpClient(){
		try {
			if (response != null) {
				response.close();
			}
			if (httpClient != null) {
				httpClient.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
